﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CamShared;
using UPnP;
using System.Threading;
using System.Reflection;
using System.Net;
using System.IO;
using System.Drawing;
using System.Diagnostics;
using DavuxLib;

namespace UpnpCameras
{
    public class Connector : ICameraConnector
    {

        List<ICamera> Cams = new List<ICamera>();

        public Connector()
        {

        }

        void u_OnDeviceDiscovered(Device d)
        {
            if (d.USN.StartsWith("CamServer"))
            {
                foreach (UpnpCamera cam in Cams)
                {
                    if (cam.myDevice.URL == d.URL)
                    {
                        return;
                    }
                }
                //Trace.WriteLine("UPnP Device: " + d.DeviceName + " at " + d.URL);
                Settings.Get(d.DeviceName + "|Show Viewer On Startup", false);
                Settings.Get(d.DeviceName + "|Unity Sharing", false);
                Settings.Get(d.DeviceName + "|Display Lag Meter", false);
                Settings.Get(d.DeviceName + "|Show Timestamp", false);
                Settings.Set(d.DeviceName + "|ProtocolVersion", d.USN.Substring("CamServer".Length).Trim());

                UpnpCamera ucam = new UpnpCamera(d);
                CameraDiscovered(ucam, this);
                Cams.Add(ucam);
            }
        }

        public void DetectCameras()
        {
            lock (UPnPDiscovery.SharedHost.DiscoveryLock)
            {
                foreach (Device d in UPnPDiscovery.SharedHost.DiscoveredDevices)
                {
                    u_OnDeviceDiscovered(d);
                }
                UPnPDiscovery.SharedHost.OnDeviceDiscovered += new UPnPDiscovery.DeviceDiscovery(u_OnDeviceDiscovered);
                if (!UPnPDiscovery.SharedHost.DiscoveryRequested)
                {
                    UPnPDiscovery.SharedHost.Discover(new SSDP());
                }
            }
        }

        public ICamera CreateCamera(string Name, string Class, string[] args)
        {
            return null;
        }

        public string Name
        {
            get { return "UPnP Camera"; }
        }


        public void Dispose()
        {
            
        }

        public event CamShared.CamShared.CameraDiscovered CameraDiscovered;



    }


    public class UpnpCamera : BaseCamera, INetworkCSCamera
    {


        public override bool ShowConfig(IntPtr handle) { return false; }

        Thread t = null;
        public Device myDevice = null;

        public int Quality { get; set; }
        public int Delay { get; set; }

        public UpnpCamera(Device dev)
        {
            myDevice = dev;

            Class = "Unity Camera";
            Name = myDevice.DeviceName;
            Quality = 100;
            Delay = 5;

            SetAllowUnsafeHeaderParsing();

            Settings.Get(Name + "|Show Timestamp", false);
            Settings.Get(Name + "|Display Lag Meter", false);

        }

        private string URLAttributes
        {
            get
            {
                return "?q=" + Quality;
            }
        }



        private void ThreadStart()
        {
            WebClient wc = new WebClient();
            while (true)
            {
                try
                {
                    //Console.WriteLine("Fetching remote camera data... " + myDevice.URL);
                    byte[] bytes = wc.DownloadData(myDevice.URL + URLAttributes);
                    MemoryStream ms = new MemoryStream(bytes);
                    if (ms.Length > 0)
                    {
                        Image img = Image.FromStream(ms);

                        if (img != null)
                        {
                            FrameArrived(this, img);
                        }
                    }
                }
                catch (ThreadAbortException)
                {
                    // we know.
                }
                catch (WebException ex)
                {
                    if (ex.Message.ToLower().Contains("unauthorized"))
                    {
                        Trace.WriteLine("Invalid User/Password");
                    }
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("UPnP Camera Error: " + ex);
                }
                Thread.Sleep(Delay);
            }
        }


        private static bool SetAllowUnsafeHeaderParsing()
        {
            //Get the assembly that contains the internal class
            Assembly aNetAssembly = Assembly.GetAssembly(
              typeof(System.Net.Configuration.SettingsSection));
            if (aNetAssembly != null)
            {
                //Use the assembly in order to get the internal type for 
                // the internal class
                Type aSettingsType = aNetAssembly.GetType(
                  "System.Net.Configuration.SettingsSectionInternal");
                if (aSettingsType != null)
                {
                    //Use the internal static property to get an instance 
                    // of the internal settings class. If the static instance 
                    // isn't created allready the property will create it for us.
                    object anInstance = aSettingsType.InvokeMember("Section",
                      BindingFlags.Static | BindingFlags.GetProperty
                      | BindingFlags.NonPublic, null, null, new object[] { });
                    if (anInstance != null)
                    {
                        //Locate the private bool field that tells the 
                        // framework is unsafe header parsing should be 
                        // allowed or not
                        FieldInfo aUseUnsafeHeaderParsing = aSettingsType.GetField(
                          "useUnsafeHeaderParsing",
                          BindingFlags.NonPublic | BindingFlags.Instance);
                        if (aUseUnsafeHeaderParsing != null)
                        {
                            aUseUnsafeHeaderParsing.SetValue(anInstance, true);
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        protected override void OnActivated()
        {
            t = new Thread(ThreadStart);
            t.Start();
        }

        protected override void OnDeActivated()
        {
            t.Abort();
        }
    }
}
